﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//内存划分为一个一个的内存单元，一个内存单元：一个字节byte
// 给每个内存单元都编号（地址）==指针
//计算机常见单位
// bit-比特位
// byte-字节（八个bit位）
// KB x1024=MB
// MB x1024
// GB
// TB
// PB
//一根地址总线传递一位电平信息0/1
//64位，64根数据线，可以传达2的64次方种地址
/*
int main()
{
	int a = 10;
	//在内存上申请四个字节的空间，存放int型的值10
	printf("%p\n", &a);//打印a的地址
	//%p是按地址的十六进制形式打印，&a取a的地址
	//16进制的形式，32位
	int* pa = &a;//pa是指针变量，用来存放(地址）指针
	//int* 是pa的类型
	return 0;
}
*/


//指针变量：pa （int*是pa的类型，&a，可以找到pa存放的地址）
// 
// 
//int* pa = &a;//存放a的首地址
// 
//pa指向的对象a是int类型
// char ch='w';//定义一个字符型：w
// char*pc=&ch;//存入pc指针变量
//*pa=20；//*是解引用操作符
// pa本身是指针，*pa加上了*号，就不是指针了，变回变量

//*p 是p指向的空间
//p是指针变量，是地址空间

//*pa=a=20；意思就是a的值被赋值为20

//指针变量（pa）的大小
// 取决于存储字长，位数，总线根数
// 32位下：32个bit位，char，int都是四个字节（指针变量的大小与类型无关）
// 64位：八个字节


// 
// 
// 
//
/*
int main()
{
	int a = 100;
	int* pa = &a;//定义pa是a的地址
	*pa = 0;//*加在pa前=a赋值为0
	printf("%d\n", a);

	  //char* p1;
	//int* p2;
	//double* p3;
	//为什么要这么多类型来存储
	return 0;
}
*/
/*
int main()
{
	int a = 0x11223344;
	//44 33 22 11四个字节
	int* pa = &a;
	//char* pa=&a;改动的只有一个字节44->00
	*pa = 0;
	//把a的四个字节，每一个字节都改成00
	
	return 0;
}
*/
//指针类型
// 决定了，对指针解引用时可访问的字节范围
// 从高位到低位
//double* 八个字节
//int* 四个字节
//short* 两个字节
//char* 一个字节
/*
int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = & a;//取出的类型应当是int，有警告
	//char* pc =(char*) & a;

	printf("%p\n", pa);//打印出地址
	printf("%p\n", pc);

	printf("%p\n", pa + 1);//比pa向后跳过了4个字节，int型
	printf("%p\n", pc + 1);//比pc向后跳过了1个字节，char型
	
	return 0;
}
*/
//指针类型决定了：解引用时有多大权限，对指针变量进行加数，减数时跳过的字节数
/*
int main()
{
	int arr[10] = { 0 };//十个四个字节的int型数据
	//创建了40个字节的连续的空间
	int i = 0;
	int* p = &arr[0];//定义指针变量，为每一个元素
	//char型，每次跳到下一个字节
	//就可以将每个字节都改变为1111 1111，对应二进制。。。
	//int型，每次跳到下四个字节，下一个元素
	//把每一个元素改变1000 1000
	for (i = 0; i < 10; i++)
	{
		*p = 1;//解引用，将数据变为1
		p++;//指针变量+1，跳过四个字节，变为下一个字节
	}
	p = &arr[0];
	//又从头开始
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
}
*/

//void*型指针，可以接收任意类型的指针
// ！！！但是不能像正常的的指针类型一样：解引用，加数减数
// 只能用于存放，接收//垃圾桶，什么都可以扔
// 
// 一般用于设计为函数的参数部分
// 
//int a = 10;
//char ch='w';
// void* pv1 = &a;
// void* pv2 = &ch;
// 
// *pv1=20;//报错：不能直接进行解引用
// pv1++;//不能加数减数操作

//const修饰指针（关键字）：常属性：赋予变量不变的属性
//
/*
int main()
{
	const int a=10;//前面加const这时，变量a不能修改，但a的本质还是变量
	//仅仅在语法上做了限制，变为不可改的值：常变量
		//a = 20;//修改不了
//通过内存改动，不直接改a这个数
	int* pa=&a;

	//const int* pa=&a;
	//可以用来修饰指针，这样，通过内存也不能再改动a了

	*pa=0;

	printf("%d\n", a);
	return 0;
}
*/
// 修饰指针时，const可以放在*左边，也可以右边
//修饰的是紧接其后的变量
/*
int main()
{
	 int a = 10;

	//const int *p = &a;//起始地址
	int const *p = &a;
	//放在*左边，限制的是*p
	//意思是不能通过指针变量*p来修改p指向的空间内容
	 *p = 0;//
	 p = &a;

	//但是此时的p还是可变量
	//不受限制 p=&a;,可以改动
    //int b = 20;
	//int * const p = &a;
	//放在*右边，限制的是变量p
	//但是此时的*p不受限制 *p=&a;,可以改动
	//*p = 0;//通过p来修改p指向的空间内容
	//p = &b;//不能改动

	//*p 是p指向的空间
	//p是指针变量，是地址空间
	printf("%d\n", a);
}
*/

// 指针的运算：
//1，指针加减整数
/*
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		//printf("%d ", *p);//只打印了起始元素
		//p+=2;
		//指针加整数，加n个整型，跳到n x4个字节
		//i<5,10个元素只用循环跳跃5次

		printf("%d ", *(p+i));//等效：循环跳过1~10个字节
		//i<10;位置改变10次
	}
	return 0;
 }
 */

// 指针减去指针
// 运算的前提条件：两个指针指向同一块空间，在连续同类的地址内运算
//指针减去指针的绝对值，是指针和指针之间的元素个数
//
//
/*
int main()
{
	//指针减去指针=地址-地址
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", &arr[9] - &arr[0]);
	//答案：9
	printf("%d\n", &arr[0] - &arr[9]);
	//-9
	return 0;
}
*/

//
/*
#include <string.h>
int my_strlen(char* s)
{
	//int count = 0;
	//while (*s!='\0')
	//{
		//count++;
		//s++;//指针加减整数
	//}
	//return count;
	char* start = s;
	while (*s)//遇到*s='\0'（'\0'ASCLL码值为0）停止
	{
		s++;
	}
	return s - start;//指针减去指针:最后一个字符到第一个字符之间的元素个数
}
int main()
{
	//strlen:求字符串长度-统计的是\0前面出现的字符个数

	int len = my_strlen("abc");
	printf("%d\n", len);
	//
	return 0;
}
*/
// arr是数组名，其实是首元素的地址=&arr[0]
/*
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int sz = sizeof(arr) / sizeof(arr[0]);
	while (p < arr + sz)//当地址小于首元素地址加上总元素个数：在数组范围内
	{
		printf("%d ", *p);//插入空格打印元素
		p++;//循环按顺序打印
	}
	return 0;
}
*/


//野指针：指针指向的地址是不可知的（随机的，不正确的，没有明确限制的） 
//1，指针一定要初始化：int* ps=&a;
// (局部变量如果不初始化，则他的值是随机的）
//（全局变量/静态变量：不初始化，默认是0）
//

// 指针访问越界
/*
int main()
{
	int arr[5] = { 0 };
	int i = 0;
	int* p = arr;
	for (i = 0; i < 10; i++)//一共就五个元素，循环数量超过了：则越界
	{
		*p = 1;
		p++;
	}
	return 0;
}
*/
// 自定义的函数内创建的变量返回的地址会在
// 函数调用完毕后销毁
// 如果这时，调用此函数，访问使用销毁后的地址，则错误❌
//so：最好不要返回局部变量的地址


//指针初始化：
// int*p1=&a;
//如果不知道指向谁：int*p2=NULL;//定义为0；后续再赋值
//                 *p=200;//报错❌：读写一个空指针NULL都会报错
// 野狗（野指针）要使用，不能直接能用，只能用树拴起来
//p=&a;才能用：

//不想用此地址时
//p = NULL;
//后续想使用时
//p = &arr[0];
//可以先判断是否野指针后使用
//if (p != NULL)






